package cn.aws360.boot.wechat.starter.controller;

import cn.aws360.boot.base.api.exception.ServiceException;
import cn.aws360.boot.base.api.pojo.response.ResultModel;
import cn.aws360.boot.base.api.utils.FileUtils;
import cn.aws360.boot.wechat.api.constant.WxmConstant;
import cn.aws360.boot.wechat.api.exception.WxmExceptionEnum;
import cn.aws360.boot.wechat.biz.service.WxMpPortalService;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.config.WxMpConfigStorage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.Map;

import static cn.aws360.boot.wechat.api.constant.WxmConstant.*;

/**
 * @author junnan
 */
@Slf4j
@Controller
@RequiredArgsConstructor
@RequestMapping("/wx/mp/portal/{appId}")
@ApiSupport(order = MODULE_WX_MP_PORTAL_CODE)
@Api(value = MODULE_WX_MP_PORTAL_NAME, tags = MODULE_WX_MP_PORTAL_TAGS)
public class WxMpPortalController {

    private final WxMpService mpService;
    private final WxMpMessageRouter mpMessageRouter;
    private final WxMpPortalService mpPortalService;

    @ResponseBody
    @GetMapping("/auth")
    @ApiOperation(value = "接收微信认证消息", hidden = true)
    public String authGet(@PathVariable String appId,
                          @RequestParam("timestamp") String timestamp,
                          @RequestParam("signature") String signature,
                          @RequestParam("nonce") String nonce,
                          @RequestParam("echostr") String echoStr) {
        log.info("接收微信认证消息：timestamp: {}, signature: {}, nonce: {}, echoStr: {}",
                timestamp, signature, nonce, echoStr);

        if (StringUtils.isAnyBlank(signature, timestamp, nonce, echoStr)) {
            throw new IllegalArgumentException("请求参数非法，请核实!");
        }

        if (!mpPortalService.checkAuth(mpService, appId, timestamp, nonce, signature)) {
            return "非法请求";
        }

        return echoStr;
    }

    @ResponseBody
    @PostMapping("/auth")
    @ApiOperation(value = "接收微信请求数据", hidden = true)
    public String authPost(@PathVariable String appId,
                           @RequestBody String requestBody,
                           @RequestParam("timestamp") String timestamp,
                           @RequestParam("signature") String signature,
                           @RequestParam("nonce") String nonce,
                           @RequestParam(name = "encrypt_type", required = false) String encType,
                           @RequestParam(name = "msg_signature", required = false) String msgSignature) {
        log.info("接收微信请求数据：timestamp: {}, signature: {}, nonce: {}, encType: {}, msgSignature: {}, requestBody: [\n{}\n]",
                timestamp, signature, nonce, encType, msgSignature, requestBody);

        if (!mpPortalService.checkAuth(mpService, appId, timestamp, nonce, signature)) {
            return "非法请求";
        }

        String out = null;
        if (StrUtil.isBlank(encType)) {
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
            WxMpXmlOutMessage outMessage = mpMessageRouter.route(inMessage);
            if (ObjectUtil.isNotNull(outMessage)) {
                log.info("组装回复用户信息, appId: {}, outMessage: {}", appId, outMessage);
                out = outMessage.toXml();
            }
        }

        if (WxmConstant.AES_KEY.equalsIgnoreCase(encType)) {
            WxMpConfigStorage configStorage = mpService.getWxMpConfigStorage();
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody,
                    configStorage, timestamp, nonce, msgSignature);
            WxMpXmlOutMessage outMessage = mpMessageRouter.route(inMessage);
            if (ObjectUtil.isNotNull(outMessage)) {
                log.info("组装回复用户信息, appId: {}, outMessage: {}", appId, outMessage);
                out = outMessage.toEncryptedXml(configStorage);
            }
        }

        return out;
    }

    @ResponseBody
    @GetMapping("/token")
    @ApiOperation(value = "获取访问令牌")
    @ApiOperationSupport(order = 1)
    @ApiImplicitParam(name = "appId", value = "应用 id", dataTypeClass = String.class, required = true)
    public ResultModel<String> getToken(@PathVariable String appId) {
        return ResultModel.ok(mpPortalService.getAccessToken(mpService.switchoverTo(appId)));
    }

    @ResponseBody
    @PostMapping("/upload/temp/{mediaType}")
    @ApiOperation(value = "上传临时文件")
    @ApiOperationSupport(order = 2)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "appId", value = "应用 id", dataTypeClass = String.class, required = true),
            @ApiImplicitParam(name = "mediaType", value = "媒体类型", dataTypeClass = String.class, required = true)
    })
    public ResultModel<Map<String, String>> uploadTemp(@PathVariable String appId, @PathVariable String mediaType,
                                                       @RequestPart("file") MultipartFile multipartFile) {
        File tmpFile = FileUtils.convert(multipartFile);
        if (ObjectUtil.isNull(tmpFile)) {
            throw new ServiceException(WxmExceptionEnum.WX_MP_UPLOAD_MEDIA_EXCEPTION);
        }

        String mediaId = mpPortalService.uploadTempMedia(mpService.switchoverTo(appId), mediaType, tmpFile);
        return ResultModel.ok(MapUtil.of("value", mediaId));
    }

}
